#include "rbuffer.h"

#include <string.h>
#include <stdlib.h>
#define rbuffer_space_len(rb) ((rb)->buffer_size - rbuffer_data_len((rbuffer_handle_t)rb))
#define ALIGN_SIZE  4
#define ALIGN_DOWN(size, align)  ((size) & ~((align) - 1))
typedef struct 
{
    uint8_t *memory_pool;   /* memory pool in which the user data is stored */
    uint32_t buffer_size;   /* buffer pool size */
    uint32_t item_count;    /* count of data that is already stored in the ring buffer */
    uint32_t head_index;    /* head index for data save */
    uint32_t tail_index;    /* tail index for data pop */
    uint8_t end_flag;
}rbuffer_device_t;

static inline uint32_t rbuffer_index_increment(uint32_t index, uint32_t capacity)
{
    return (index + 1) % capacity;
}


//static inline uint32_t rbuffer_index_increments(uint32_t index, uint32_t size, uint32_t capacity)
//{
//    return (index + size) % capacity;
//}

/**
 * get the size of data in rb
 */
uint32_t rbuffer_data_len(rbuffer_handle_t handle)
{
    if(handle == NULL) {
        return 0;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    return rb->item_count;
}

/**
 * get the size of data in rb
 */
uint32_t rbuffer_data_len_set(rbuffer_handle_t handle, uint32_t length)
{
    if(handle == NULL) {
        return 0;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
//    rb->head_index = rbuffer_index_increments(rb->head_index, length, rb->buffer_size);
    rb->item_count += (rb->buffer_size - length);
    return rb->item_count;
}

uint8_t rbuffer_end_flag_set(rbuffer_handle_t handle)
{
     if(handle == NULL) {
        return 0;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    rb->end_flag = 1;
    return rb->end_flag;
}

uint8_t rbuffer_end_flag_get(rbuffer_handle_t handle)
{
    if(handle == NULL) {
        return 0;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    if (0 != rb->end_flag) {
        rb->end_flag = 0;
        return 1;
    }
    return 0;
}

int32_t is_rbuffer_empty(rbuffer_handle_t handle) 
{
    if(handle == NULL) {
        return -1;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    return (0 == rb->item_count); 
}

int32_t is_rbuffer_full(rbuffer_handle_t handle)
{
    if(handle == NULL) {
        return -1;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    return (rb->buffer_size == rb->item_count);
}

rbuffer_handle_t rbuffer_create(rbuffer_config_t *config)
{
    // RT_ASSERT(rb != RT_NULL);
    // RT_ASSERT(size > 0);

    /* initialize read and write index */
    if(config == NULL) {
        return NULL;
    }
    
    if(config->buffer_size == 0) {
        return NULL;
    }
    
    rbuffer_device_t *device = (rbuffer_device_t *)malloc(sizeof(rbuffer_device_t));
    if(device == NULL) {
        return NULL;
    }
    device->buffer_size = config->buffer_size;
    
    device->item_count = device->head_index = device->tail_index = 0;

    /* set buffer pool and size */
    if (NULL != config->static_mem) {
        device->memory_pool = config->static_mem;
    } else {
        device->memory_pool = malloc(device->buffer_size);
    }
    if(device->memory_pool == NULL) {
        free(device);
        return NULL;
    }
    return (rbuffer_handle_t)(device);
}

/**
 * put a character into r buffer
 */
int32_t rbuffer_put_char(rbuffer_handle_t handle, const uint8_t ch)
{
    // RT_ASSERT(rb != RT_NULL);
    if(handle == NULL) {
        return -1;
    }
    if (is_rbuffer_full(handle)) {
        return -2;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    /* whether has enough space */
    rb->memory_pool[rb->head_index] = ch;
    rb->item_count ++;
    rb->head_index = rbuffer_index_increment(rb->head_index, rb->buffer_size);
    return 0;
}

/**
 * put str into r buffer
 */
int32_t rbuffer_put_str(rbuffer_handle_t handle, const uint8_t *w_str, uint16_t length)
{
    for(uint16_t i = 0; i < length; i++) {
        if(0 != rbuffer_put_char(handle, w_str[i])) {
            return -1;
        }
    }
    return 0;
}
/**
 * get a character from a rbuffer
 */
int32_t rbuffer_get_char(rbuffer_handle_t handle, uint8_t *ch)
{
    // RT_ASSERT(rb != RT_NULL);
    if(handle == NULL) {
        return -1;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    /* rbuffer is empty */
    if (!rbuffer_data_len(handle))
        return -2;

    /* put character */
    *ch = rb->memory_pool[rb->tail_index];

    rb->item_count--;
    rb->tail_index = rbuffer_index_increment(rb->tail_index, rb->buffer_size);
    return 0;
}

/**
 * get str from a rbuffer
 */
int32_t rbuffer_get_str(rbuffer_handle_t handle, uint8_t *r_str, uint16_t length)
{
    uint32_t data_len = rbuffer_data_len(handle);
    if (0 == data_len) {
        return 0;
    }
    if(length > data_len) {
         length = data_len;
    }
    for(uint16_t i = 0; i < length; i++) {
        if(0 != rbuffer_get_char(handle, (r_str + i))) {
            return i;
        }
    }
    return length;
}

int32_t rbuffer_get_size(rbuffer_handle_t handle)
{
    if(handle == NULL) {
        return -1;
    }
    
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    return rb->buffer_size;
}
void *rbuffer_mempool_get(rbuffer_handle_t handle)
{
    if(handle == NULL) {
        return NULL;
    }
    
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    return rb->memory_pool;
}

int32_t rbuffer_destroy(rbuffer_handle_t handle) 
{
    if(handle == NULL) {
        return 0;
    }
    rbuffer_device_t *rb = (rbuffer_device_t *)handle;
    if(rb->memory_pool != NULL) {
        free(rb->memory_pool);
    }
    free(rb);
    return 0;
}


